using System;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;
using My.IdlConverter.Core;
using My.IdlConverter.Core.Types;
using My.IdlConverter.Core.Util;
using My.IdlConverter.Core.Parsers;

namespace My.IdlConverter.Com
{
    public delegate void ParseInterfaceDelegate(object sender,Interface interfaceObj);

    public class ComIdlParser : ParserBase
    {
        public static string IdlInterfacePattern = @"interface\s*\w*\s*:\w*\s*\w*\s*(\r\n)+\{.*?\}";
        public static string InterfaceIdentityPattern = @"interface(\s*\w*\s*):\s*\w*\s*";
        //public static string IdlMathodPattern = @"\s*.*HRESULT\s+([\w,\d]+)(\(.*\))";
        public static string IdlMathodPattern = @"(\s*.*)helpstring\((.*)\).*HRESULT\s+([\w,\d]+)(\(.*\))";
        public static string IdlParameterPattern = @"\[(.*?)\](.*?)[,|;]";
        public event ParseInterfaceDelegate InterfaceParsing;

        private static IType DefaultRetType = new SimpleType();

        public ComIdlParser()
        {
        }

        public override object Parse(string idlFile)
        {
            IList<Interface> interfaceList = new List<Interface>();

            string content;
            if (!IOHelper.ReadToEnd(idlFile, out content))
                throw new System.IO.IOException(String.Format("IO Excetion when read : {0}",idlFile));

            IPreprocessor<string> preprocessor = new InterfacePreprocessor();
            preprocessor.Consumer = content;
            preprocessor.Process();

            Regex reg = new Regex(IdlInterfacePattern, RegexOptions.Singleline);
            MatchCollection matches = reg.Matches(content);
            foreach (Match nextMatch in matches)
            {
                reg = new Regex(InterfaceIdentityPattern);
                Match match = reg.Match(nextMatch.Value);
                Group group = match.Groups[1];
                Interface idlInterfaceObj = new Interface(group.Value.Trim());
                interfaceList.Add(idlInterfaceObj);

                if (InterfaceParsing != null)
                    InterfaceParsing(this,idlInterfaceObj);

                Regex methodReg = new Regex(IdlMathodPattern);
                MatchCollection methodMatches = methodReg.Matches(nextMatch.Value);
                foreach (Match nextMethodMatch in methodMatches)
                {
                    Method idlMethodObj = ParseIdlMethod(nextMethodMatch);
                    if (idlMethodObj != null)
                    {
                        idlMethodObj.Parent = idlInterfaceObj;
                        idlInterfaceObj.Methods.Add(idlMethodObj);
                    }
                }
            }

            return interfaceList;
        }

        private Method ParseIdlMethod(Match nextMethodMatch)
        {
            Group getterOrSetterGroup = nextMethodMatch.Groups[1];
            Group methodNoteGroup = nextMethodMatch.Groups[2];
            Group methodGroup = nextMethodMatch.Groups[3];
            ComMethod idlMethodObj = new ComMethod(DefaultRetType, methodGroup.Value.Trim());
            idlMethodObj.Note = methodNoteGroup.Value.Trim(new char[] { '"','"',' '});
            if (getterOrSetterGroup.Value.IndexOf("propget") != -1)
                idlMethodObj.Getter = true;
            else if (getterOrSetterGroup.Value.IndexOf("propput") != -1)
                idlMethodObj.Setter = true;
            Group parametersGroup = nextMethodMatch.Groups[4];
            string parametersString = parametersGroup.Value.Trim();
            if (parametersString.Trim()[parametersString.Length - 1] != ';')
                parametersString = parametersString + ";";
            if (parametersString.Length > "()".Length)
            {
                Regex paraReg = new Regex(IdlParameterPattern);
                MatchCollection paraMatches = paraReg.Matches(parametersString);
                if (paraMatches.Count > 0)
                {
                    foreach (Match nextParaMatch in paraMatches)
                    {
                        string paraProp = nextParaMatch.Groups[1].Value.Trim();
                        string paramTypeAndName = nextParaMatch.Groups[2].Value.Trim();

                        int spacePos = paramTypeAndName.LastIndexOf(' ');
                        string paramType = paramTypeAndName.Substring(0, spacePos).Trim(new char[] { ' ', ')' });
                        string paramName = paramTypeAndName.Substring(spacePos).Trim(new char[] { ' ', ')' });

                        Parameter paramObj = new ComSpecialParameter(parametersGroup.Value);
                        paramObj.Attribute = ParseParamAttribute(paraProp);
                        paramObj.Name = paramName;
                        paramObj.Type = new ComSpecialType(paramType);
                        paramObj.Parent = idlMethodObj;

                        if (paramObj.Attribute == ParamAttribute.Out)
                            idlMethodObj.ReturnType = new ComSpecialType(paramType);
                        else
                            idlMethodObj.Parameters.Add(paramObj);
                    }
                }
            }

            return idlMethodObj;
        }

        private ParamAttribute ParseParamAttribute(string paraProp)
        {
            if (paraProp.Equals("in", StringComparison.OrdinalIgnoreCase))
                return ParamAttribute.In;
            else if (paraProp.Equals("out", StringComparison.OrdinalIgnoreCase))
                return ParamAttribute.Out;
            else if (paraProp.Equals("out,retval", StringComparison.OrdinalIgnoreCase))
                return ParamAttribute.Out;
            else if (paraProp.Equals("in,out", StringComparison.OrdinalIgnoreCase))
                return ParamAttribute.InOut;
            else
            {
                System.Diagnostics.Debug.Assert(false);
                return ParamAttribute.Unknown;
            }
        }
    }
}
